package Simplification;

import java.util.LinkedList;

import Syntaxe.Expression;
import Syntaxe.Fraction;
import Syntaxe.Invariant;
import Syntaxe.Matrice;
import Syntaxe.Simplexe;
import Syntaxe.Vecteur;

public class Simplification {
	Invariant inv;
	// 1 suppose tous >=; variables ordered from X0 to Xn-1 (coefficant 0 if not
	// existed);
	// 2 suppose all X>=0
	// 3 suppose all b>=0

	Fraction[][] matrice;// dim: (index+1)*(N+index+1)
	// each line: a0,a1,a2,...,a(n-1),c
	public Simplexe simplexe;

	int numVar;// number of variables
	int numExpr;// number of inequations
	Expression expr;// to see if f=expr in inv has min>=0
	double[] fonction;// fonction to be find the min

	// donc matrice est de taille: index*(N+1)

	public Simplification(Simplexe sim) {

		simplexe = sim;
		matrice = sim.matrice;
		numExpr = sim.numExpr;
		numVar = sim.numVar;

	}

	public static Simplexe matriceIni(Matrice m, Fraction[] f) {
		// depuis une systeme de matrice standard
		// from Ax>=b, x>=0,b>=0
		int M = m.M;
		int N = m.N;
		Fraction[][] sim = new Fraction[M + 1][M + N];

		for (int i = 0; i < M; i++) { // S_N
			for (int j = 0; j < N - 1; j++) {
				sim[i][j] = m.matrice[i][j].multiple(-1);
			}
		}

		for (int i = 0; i < M; i++) { // S_B base
			for (int j = 0; j < M; j++) {
				if (i == j) {
					sim[i][N - 1 + j] = new Fraction(1);
				} else {
					sim[i][N - 1 + j] = new Fraction(0);
				}
			}
		}
		for (int i = 0; i < M; i++) { // B^-1 b
			sim[i][N + M - 1] = m.matrice[i][N - 1].multiple(-1);
		}

		for (int j = 0; j < f.length; j++) { //
			sim[M][j] = f[j].multiple(-1);
		}
		for (int j = 0; j < N - f.length; j++) {
			sim[M][f.length + j] = new Fraction(0);
		}
		for (int j = 0; j < M; j++) {
			sim[M][N + j] = new Fraction(0);
		}

		return new Simplexe(new Matrice(sim), f);

	}

	public static Simplexe Step1(Matrice m) {
		// depuis une systeme de matrice standard AX=b
		// AX+T=b
		// min f(T)=t1+...tn
		int M = m.M;
		int N = m.N;
		Fraction[][] sim = Matrice.zeros(M + 1, M + N).matrice;

		for (int i = 0; i < M; i++) { // S_N
			for (int j = 0; j < N - 1; j++) {
				sim[i][j] = m.matrice[i][j];
			}
		}

		for (int i = 0; i < M; i++) { // S_B base
			for (int j = 0; j < M; j++) {
				if (i == j) {
					sim[i][N - 1 + j] = new Fraction(1);
				} else {
					sim[i][N - 1 + j] = new Fraction(0);
				}
			}
		}
		for (int i = 0; i < M; i++) { // B^-1 b
			sim[i][N + M - 1] = m.matrice[i][N - 1];
		}

		for (int j = 0; j < N - 1; j++) { // det

			for (int i = 0; i < M; i++) {
				sim[M][j] = sim[M][j].plus(sim[i][j]);
			}
		}
		for (int j = 0; j < M; j++) {
			sim[M][N - 1 + j] = new Fraction(0);
		}

		// f=t1+...+t_numExpr
		Fraction[] f = Vecteur.zeros(N + M - 1);
		for (int i = 0; i < M; i++) {
			f[i + N - 1] = new Fraction(1);
		}
		return new Simplexe(new Matrice(sim), f);
	}

	public Fraction Mini() {
		// to see if f=expr in inv has min>=0
		// realisation de methode de Simplexe
		// to minimise
		// f0, ...,fn-1,C
		// to initialise expr, s.t. dim=[this.index+this.N]

		// to show base
		// simplexe.afficheBase();

		// Fraction[][] M = matrice.clone();
		// Matrice mat = new Matrice(M);

		// /////to debug
		simplexe.AfficheMatrice();

		// find main Colonne
		int mainC = MainColonne();

		// ///////to debug
		System.out.print("!!" + mainC);
		matrice[numExpr][mainC].print();
		System.out.println();

		if (Check(mainC)) {
			return simplexe.valeur();
		} else {
			int mainL = MainLigne(mainC);
			// transformation de ligne, t.q. M[mainL][mainC]=1,
			// M[other][mainC]=0

			// TODO //Maybe not true
			// mat.LigneUnify(mainL, mainC);
			// simplexe.base[mainL]=mainC;
			simplexe.changeBase(mainL, mainC);

			// to show the process
			// mat.AfficheMatrice();

			// Simplexe sim = new Simplexe(mat);
			// Simplification s = new Simplification(sim);

			// simplexe= new Simplexe(mat,simplexe.base);
			Simplification s = new Simplification(simplexe);
			return s.Mini();
		}
	}

	public int MainColonneExpr(Expression expr) {
		// to find the main colonne: i.e. the largest

		int num = 0;// index of the biggest number
		LinkedList<Double> temp = new LinkedList<Double>();
		temp = expr.toDoubleList();
		temp.pollLast();// remove C

		double big = temp.peek();
		int i = 0;

		while (!temp.isEmpty()) {
			double a = temp.poll();
			if (big < a) {
				big = a;
				num = i;
			}
			i = i + 1;
		}
		return num;
	}

	public int MainColonne() {
		// to find the main colonne

		// matrice

		int MainC = 0;
		Fraction d = matrice[numExpr][0];
		for (int i = 1; i < numExpr + numVar; i++) {
			if (matrice[numExpr][i].PlusGrandQue(d)) {

				d = matrice[numExpr][i];
				MainC = i;
			}
		}
		return MainC;
	}

	public boolean Check(int id) {
		// dans la ligne det, det[mainC] <0: deja mini

		if (matrice[numExpr][id].strictPositive()) {
			return false;
		}
		return true;
	}

	public int MainLigne(int id) {
		// quand on sait le main colonne est id eme, cherche main ligne
		Fraction d = new Fraction(1000000); // valeur initiale=+INFINIY
		int ligne = 0;
		for (int j = 0; j < numExpr; j++) {
			if (matrice[j][id].strictPositive()) {

				Fraction temp = matrice[j][numVar + numExpr]
						.divise(matrice[j][id]);
				if (!temp.PlusGrandQue(d)) {
					d = temp;
					ligne = j;
				}
			}

		}
		return ligne;

	}

	public static Fraction minimise(Matrice m, Fraction[] f) {

		// After PreSimplification
		// m=[A,b]

		Simplexe sim = Step1(m);

		// /////////////
		System.out.println("begin step1");
		sim.AfficheDouble();

		Simplification simp = new Simplification(sim);
		simp.Mini();
		Fraction[] valeurs = simp.simplexe.getValeurs();

		// ////////to debug
		// result of step1
		// sim.AfficheDouble();
		Vecteur.affiche(valeurs);

		boolean check = true;// check if inv is not null set
		for (int i = 0; i < simp.numExpr; i++) {

			if (!valeurs[i + simp.numVar].isZero()) {
				check = false;
				System.out.println("there is no point in the system");
			}
		}

		// ///////////////////////
		// step2
		if (check == true) {
			System.out.println("begin step2");
			simp.simplexe.AfficheDouble();

			Fraction[] det = simp.simplexe.ligneDetermine();

			for (int i = 0; i < simp.numExpr; i++) {

				if (det[i + simp.numVar].isZero()) {
					for (int k = 0; k < simp.numExpr; k++) {

						// simp.simplexe.AfficheMatrice();

						// simp.simplexe.LigneUnify(k, i+simp.numVar);
						simp.simplexe.changeBase(k, i + simp.numVar);
					}
				}
			}
		}
		simp.simplexe.AfficheMatrice();
		// from step1 to step2
		Matrice newMatrice = simp.simplexe.subColonne(0, simp.numVar - 1);
		newMatrice = newMatrice.fusion(newMatrice,
				simp.simplexe.colonne(simp.simplexe.N - 1));
		newMatrice = newMatrice.subLigne(0, newMatrice.M - 2);// TODO
																// Attention!!!

		System.out.println("where i am");
		newMatrice.AfficheMatrice();

		Simplexe newSimplexe = new Simplexe(newMatrice, f, simp.simplexe.base);
		Simplification newSimp = new Simplification(newSimplexe);

		Fraction min = newSimp.Mini();

		// /////////////debug
		System.out.println("the result is " + min.toDouble());
		// newSimp.simplexe.AfficheMatrice();

		return min;

	}

	public static boolean isEmpty(Matrice m, Fraction[] f) {

		// After PreSimplification
		// m=[A,b]

		Simplexe sim = Step1(m);

		// /////////////
		System.out.println("begin step1");
		sim.AfficheDouble();

		Simplification simp = new Simplification(sim);
		simp.Mini();
		Fraction[] valeurs = simp.simplexe.getValeurs();

		// ////////to debug
		// result of step1
		// sim.AfficheDouble();
		Vecteur.affiche(valeurs);

		boolean check = true;// check if inv is not null set
		for (int i = 0; i < simp.numExpr; i++) {

			if (!valeurs[i + simp.numVar].isZero()) {
				check = false;
				System.out.println("there is no point in the system");
				return true;
			}
		}
		return false;
	}

}
